/*
  循环比赛日程表
  题目描述
    设有 N (N ≤ 1000) 个选手进行循环比赛，其中 N = 2^M，
    要求每名选手要与其他 N − 1 名选手都赛一次，每名选手每天比赛一次，
    循环赛共进行 N − 1 天，要求每天没有选手轮空。
  输入格式
    一个数 M。
  输出格式
    输出：表格形式的比赛安排表。一行各数据间用一个空格隔开。
  输入数据 1
    3
  输出数据 1
    1 2 3 4 5 6 7 8
    2 1 4 3 6 5 8 7
    3 4 1 2 7 8 5 6
    4 3 2 1 8 7 6 5
    5 6 7 8 1 2 3 4
    6 5 8 7 2 1 4 3
    7 8 5 6 3 4 1 2
    8 7 6 5 4 3 2 1
  样例解释：
          第一天  第二天  第三天  第四天  第五天  第六天  第七天
      1     2      3       4   |   5      6       7      8
      2     1      4       3   |   6      5       8      7
      3     4      1       2   |   7      8       5      6
      4     3      2       1   |   8      7       6      5
    --------------------------------------------------------
      5     6      7       8   |   1      2       3      4
      6     5      8       7   |   2      1       4      3
      7     8      5       6   |   3      4       1      2
      8     7      6       5   |   4      3       2      1
*/

#include <bits/stdc++.h>

using namespace std;

int a[1001][1001] = {};

/*
  为目标正方形赋值，正方形每个格子中的值为原数据的正方形格子中的值
  输入参数说明:
    s_x, s_y -- 存放原数据的正方形的左上角的坐标(横坐标、纵坐标)，
    d_x, d_y -- 存放目标正方形的左上角的坐标(横坐标、纵坐标)，
    len -- 正方形的边长
*/
void copy_data(int s_x, int s_y, int d_x, int d_y, int len) {
    for (int i = 0; i < len; i++) {
        for (int j = 0; j < len; j++) {
            a[d_y + i][d_x + j] = a[s_y + i][s_x + j];
        }
    }
}

/*
  使用分治的方法，对正方形格子中的每个元素进行赋值
  输入参数说明：
    x1 -- 正方形左上格子的横坐标
    y1 -- 正方形左上格子的纵坐标
    x2 -- 正方形右下格子的横坐标
    y2 -- 正方形右下格子的纵坐标
  注意: 使用分治的方法进行编码时，通常会用到递归!
*/
void f(int x1, int y1, int x2, int y2) {

    if (x1 == x2 && y1 == y2) {
        a[y1][x1] = y1;  // 注意: 这里
    } else {
        /* 将该正方形分成 4 个子正方形 */

        // 1. 对左上角的 子正方形中的每个元素进行赋值 (递归)
        f(x1, y1, x1+(x2-x1+1)/2-1, y1+(y2-y1+1)/2-1);

        // 2. 对左下角的 子正方形中的每个元素进行赋值 (递归)
        f(x1, y1+(y2-y1+1)/2, x1+(x2-x1+1)/2-1, y2);

        // 3. 给 右上角的子正方形中的每个元素进行赋值
        //  a). 确定目标（右下）正方形的第一个格子的坐标: (x1+(x2-x1+1)/2, y1+(y2-y1+1)/2)
        //  b). 确定原数据 (左上) 正方形的第一个格子的坐标: (x1, y1)
        //  c). 确定正方形的边长: (x2-x1+1)/2
        //  d). 调用 copy_data() 函数进行赋值
        copy_data(x1, y1, x1+(x2-x1+1)/2, y1+(y2-y1+1)/2, (x2-x1+1)/2);

        // 4. 给 右下角的子正方形中的每个元素进行赋值
        //  a). 确定目标（右上）正方形的第一个格子的坐标: (x1+(x2-x1+1)/2, y1)
        //  b). 确定原数据 (左下) 正方形的第一个格子的坐标: (x1, y1+(y2-y1+1)/2)
        //  c). 确定正方形的边长: (x2-x1+1)/2
        //  d). 调用 copy_data() 函数进行赋值
        copy_data(x1, y1+(y2-y1+1)/2, x1+(x2-x1+1)/2, y1, (x2-x1+1)/2);
    }
}

int main() {
    int m;

    cin >> m;
    int n = pow(2, m);

    // 对正方形格子中的每个元素进行赋值, 其中, 该正方形的左上角坐标为 (1, 1), 右下角坐标为 (n, n)。
    f(1, 1, n, n);

    for (int i = 1; i <= n; i++) {
        for (int j =1 ; j <= n; j++) {
            cout << a[i][j] << " ";
        }
        cout << endl;
    }

    return 0;
}